
package fr.lelouet.monitoring.stress
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.hyperic.sigar.Sigar;



class CPUStress implements Runnable {
	
	private static final Logger logger = LoggerFactory.getLogger(CPUStress.class)
	
	/** number of cycles to pass on computing before giving back hand to the main program*/
	long loopSize=10
	
	protected boolean hasToStop=false
	
	/** only used to force processor action*/
	protected def assigned=0
	
	public void run() {
		hasToStop=false
		while(!hasToStop) {
			loopSize.times { assigned++ }
			Thread.sleep 1
		}
	}
	
	public void stop() {
		hasToStop=true
	}
	
	/** do some stress actions. The stresses start at a zero lvl, and is multiplied per 2 each time. it generates one stresser thread per CPU **/
	static void main(args) {
		CPUStress stress = new CPUStress()
		stress.calibrateLoopSize().each{
			System.out.println("$it is a good value")
		}
		stress.stop()
	}
	
	/** the minimum power of 2 to start bench with*/
	int minBenchPow = 5
	
	/** minimum idle CPU value to consider that a parameter is interesting while calibrating*/
	double minIdle=0.10
	
	/** maximum idle CPU value to consider that a parameter is interesting while calibrating*/
	double maxIdle = 0.85
	
	/** while calibrating, start accepting all data after that many parameters have given an accepted idleness, and stop accepting all data after that many parameters have not been accepted*/
	int successiveChecksToAccept = 3
	
	/**
	 * test several values for the {@link #loopSize} parameter<br />
	 * start from 2^ #minBenchPow , multiplying tested value by 2<br />
	 * each value is tested with {@link Sigar} to get the idleness of CPU under such a load<br />
	 * The calibration stops after #successiveChecksToAccept successive parameters have been &lt; #maxIdle , and  then #successiveChecksToAccept have been &lt; #minidle
	 * @return a list of parameters that have meaning for generating different CPU load.
	 * Those parameters can be given to {@link #loopSize}*/
	public List<Long> calibrateLoopSize(long delay_ms=500) {
		List<Long> ret = [0]
		Sigar sigar = new Sigar()
		Thread.sleep(delay_ms)
		int nbThreads=sigar.getCpuList().size()
		CPUStress stress = this
		nbThreads.times {
			Thread.start { stress.run() }
		}
		Long param = Math.pow(2, minBenchPow) as Long
		logger.trace("   waiting for pass")
		int nbpass=0
		while(nbpass<successiveChecksToAccept) {
			setLoopSize param
			Thread.sleep(delay_ms)
			def idle = sigar.getCpuPerc().getIdle()
			logger.debug("{} => {} idle", param, idle)
			if (idle<=maxIdle) {
				nbpass++
				ret.add(param)
			} else {
				nbpass=0
			}
			param *=2
		}
		logger.trace "   waiting for fail"
		int nbfail=0
		while(nbfail<successiveChecksToAccept) {
			setLoopSize param
			Thread.sleep(delay_ms)
			def idle = sigar.getCpuPerc().getIdle()
			logger.debug("{} => {} idle", param, idle)
			if (idle>=minIdle) {
				nbfail=0
				ret.add(param)
			} else {
				nbfail++
			}
			param *=2
		}
		stop()
		return ret
	}
}
